#include <stdio.h>
#include <stdlib.h>
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h>
#include <poll.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "net.h"

#define FLAG_URG        /*   (紧急):紧急报文需尽快传送，pointer指向紧急报文的最后一个字节   */
#define FLAG_ACK        /*   (确认):ACK=1时，确认序号字段才有效   */
#define FLAG_PSH        /*   (传送):PSH=1，需尽快将报文传递给应用程序   */
#define FLAG_RST        /*   (重置):RST=1，表示连接出错，必须释放连接   */
#define FLAG_SYN        /*   (建立联机):SYN=1,ACK=0时，表示请求建立连接   */
#define FLAG_FIN        /*   (结束):FIN，发送端完成发送任务   */
/**
 * @brief TCP 三次握手
 * 
 * 1. 客户端发送请求报文： SYN=1 seq=x
 *    TCP规定 SYN=1 的报文不能携带数据，随机生成一个序列x
 * 
 * 2. 服务端发送确认报文： ACK=1 SYN=1 ack=x+1 seq=y
 *    TCP规定回复报文不能携带数据 ack=x+1 再产生一个序列y
 * 
 * 3. 客户端发送确认报文： ACK=1 ack=y+1
 *    TCP规定ACK报文可以携带数据，不携带则不消耗序列号
 *    客户端进入 ESTABLISHED状态
 */
struct tcp_hdr {
    uint16_t s_port;
    uint16_t d_port;
    uint32_t seq;
    uint32_t ack;
    uint8_t zero_0:4,
            off:4;
    uint8_t flag:6,
            zero_1:2;
    uint16_t win;       /*   滑动窗口，是接收端所能提供的最大的缓冲区大小   */
    uint16_t check;
    uint16_t pointer;   /*   紧急指针， 是一个正的偏移量指向最后一个字节   */
    uint16_t opt[0];
}__attribute__((packed));
/**
 * @brief TCP 数据传输分为两种情况：正常 / 异常
 * 
 * TCP 的数据传输：
 *      角色--- 发送端和接收端
 *      数据--- 发送/接收缓冲区
 * 
 *      发送端A  >>>生成一个序列号 seq=40 ，再加上一个报文大小 win=10 >>>   接收端B
 *              <<<回复一个ACK, ack=40+10 再加上一个期望报文大小 5<<<
 *              >>>发送 seq=50, win=5>>>
 *              ...
 * 
 *      下面从发送端和接收端分别考虑报文异常的情况各自的处理：
 *       发送端：定时器时间内一直没收到对应的ack，则重传
 *       接收端：发送的数据为 [40,10)，直接收到了 [40, 5)
 *              则ack回复：[45, 10)
 *       发送端收到ack则发送： [45, 10)
 * 
 * 
 * 发送端 - 拥塞控制：
 *    拥塞窗口：发送方维持一个叫做拥塞窗口 cwnd的状态变量。拥塞窗口的大小取决于网络的拥塞程度，并且动态变化。
 *    发送窗口=min(cwnd，接受端接收窗口大小)
 *      总结：tcp的报文收发，是受到接收端控制的
 *           通过 seq 序列号 + win大小 进行报文控制
 */



/**
 * @brief 慢启动
 * 
 * 描述：禁止tcp连接上，立即传输大包
 * 原因：在网络拥塞的时候，大量的大包会阻塞在路由器上切片/转发。容易导致网络路由缓存耗尽
 * 内容：新建立的练级从小包开始发，逐渐增大再稳定确认合适的大小发送
 * 
 * cwnd: 每收到一个ack确认，cwnd 成2的幂次方增加
 *       当 cwnd > ssthresh(慢启动门限) 慢启动过程结束，进入拥塞避免
 * 
 * @brief 拥塞避免    
 * cwnd: 当前窗口所有报文被确认时，cwnd + 1进行线性增长
 *       如果发生一次超时：ssthresh/=2, cwnd = 1重新进入慢启动
 * 
 * @brief 快速重传
 * 接收端只会ACK确认最后一个收到的连续包：
 * 如: 发送端                       接收端
 *     1.2.3.4.5    2丢失          每收到一个包 ack2回去
 *     发送端收到3个2，则重传         收到2后 ack6 
 * 
 */